// --------------------------------------------------------------------------------------------------------------------
// <copyright file="HotKeyCatcher.cs" company="WordSteps.com">
//   All rights reserved
// </copyright>
// <summary>
//   Defines the HotKeyCatcher type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace RapidDict.Modules.HotKeyService.Utils
{
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    using RapidDict.Infrastructure.Interfaces;

    public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

    internal class HotKeyCatcher
    {
        private readonly IAppViewService appViewService;
        private readonly ITranslationService translationService;

        private readonly LowLevelKeyboardProc hookProc;
        private IntPtr hookId;

        private bool controlKeyIsPressed;
        private int pressedCountOfCKey;
        private int pressedCountOfInsKey;
        
        public HotKeyCatcher(IAppViewService appViewService, ITranslationService translationService)
        {
            this.appViewService = appViewService;
            this.translationService = translationService;
            this.hookProc = this.KeyboardProc;
        }

        public void SetKeyboardHook()
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                if (curModule == null)
                {
                    throw new ApplicationException("MainModule of CurrentProcess not found");
                }
                    
                this.hookId = Win32.SetWindowsHookEx(Win32.WH_KEYBOARD_LL, this.hookProc, Win32.GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        public void UnhookKeyboard()
        {
            if (this.hookId != IntPtr.Zero)
            {
                Win32.UnhookWindowsHookEx(this.hookId);
                this.hookId = IntPtr.Zero;
            }
        }

        protected void ShowTranslationFromClipboard()
        {
            if (Clipboard.ContainsText())
            {
                string selectedText = Clipboard.GetText();

                // Set captured text for translation and maximize Window from tray
                this.translationService.SetWord(selectedText);
                this.appViewService.ShowApp();
            }
        }

        private IntPtr KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            // Procceed hook
            if (nCode >= 0) 
            {
                var message = (WindowsMessages)wParam;
                var virtualKeyCode = (VirtualKeys)Marshal.ReadInt32(lParam);

                switch (message)
                {
                    // Key pressed
                    case WindowsMessages.WM_KEYDOWN: 
                    {
                        // Check only left or right Control key
                        if ((virtualKeyCode == VirtualKeys.LeftControl) || (virtualKeyCode == VirtualKeys.RightControl))
                        {
                            this.controlKeyIsPressed = true;
                        }
                            
                        break;
                    }

                    // Key released
                    case WindowsMessages.WM_KEYUP: 
                        {
                            // If Control key released, then user changed his descision to translate
                            if ((virtualKeyCode == VirtualKeys.LeftControl) || (virtualKeyCode == VirtualKeys.RightControl))
                            {
                                this.controlKeyIsPressed = false;
                                this.pressedCountOfInsKey = this.pressedCountOfCKey = 0;
                                break;
                            }

                            if (virtualKeyCode == VirtualKeys.C)
                            {
                                // If "C" is released and Control is still pressed - maybe user wants to translate
                                if (this.controlKeyIsPressed)
                                {
                                    this.pressedCountOfCKey++;
                                    this.pressedCountOfInsKey = 0;
                                }

                                break;
                            }

                            if (virtualKeyCode == VirtualKeys.Insert)
                            {
                                // If "C" is released and Control is still pressed - maybe user wants to translate
                                if (this.controlKeyIsPressed)
                                {
                                    this.pressedCountOfInsKey++;
                                    this.pressedCountOfCKey = 0;
                                }

                                break;
                            }

                            // If any other key pressed - lets reset counters
                            this.pressedCountOfInsKey = this.pressedCountOfCKey = 0; 
                            break;
                        }
                }

                // Check if hot keys are pressed
                if (this.pressedCountOfCKey == 2 || this.pressedCountOfInsKey == 2)
                {
                    // Reset C and Ins counters independenly if user released CTRL or not
                    this.pressedCountOfInsKey = this.pressedCountOfCKey = 0;
                    this.ShowTranslationFromClipboard();
                }
            }

            return Win32.CallNextHookEx(this.hookId, nCode, wParam, lParam);
        }
    }
}